package com.example.synchronizedLearning;

public class Syn1 {
    /**
     * synchronized 作用于实例方法，表示锁住当前对象（this）。
     * 线程在执行该方法前，必须先获取当前对象的锁。
     * <p>
     * this 指的是当前对象的引用。
     * 线程在访问 synchronized 的实例方法或代码块之前，必须先持有该对象的锁（对象头中的 Monitor）。
     * 如果对象锁已经被其他线程持有，当前线程会被阻塞，直到该对象锁被释放。
     */
    public synchronized void print() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "-" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

/**
 * 场景	        解释
 * 锁住当前对象	synchronized 修饰实例方法，锁住 this
 * 锁住不同对象	不同对象实例，互不影响
 * 锁住静态方法	锁住类对象，所有对象共享同一把锁
 * 代码块锁	    synchronized(this) 锁住当前对象
 */
class Syn1Test {
    public static void main(String[] args) {

        /**
         * 同一个对象
         */
        Syn1 syn1 = new Syn1();

        Thread thread1 = new Thread(() -> syn1.print());
        Thread thread2 = new Thread(() -> syn1.print());
        thread1.setName("thread1");
        thread2.setName("thread2");

        /**
         * thread1 和 thread2 都在调用同一个对象 syn1 的 print() 方法。
         * synchronized 锁住的是当前对象（即 syn1）。
         * thread1 首先获取了 syn1 的锁，执行 print() 方法。
         * thread2 必须等待 thread1 执行完毕并释放锁之后，才能获取对象锁并执行 print() 方法。
         */

        thread1.start();
        thread2.start();

        System.out.println(thread1.getName() + ":" + thread1.getState());
        System.out.println(thread2.getName() + ":" + thread2.getState());

        /**
         * 不同对象
         */
        Syn1 obj1 = new Syn1();
        Syn1 obj2 = new Syn1();

        /**
         * t1 和 t2 分别锁住不同对象 obj1 和 obj2。
         * 由于锁住的是不同的对象，两个线程不会互相影响。
         * 线程 t1 和 t2 可以并行执行。
         */

        Thread t1 = new Thread(() -> obj1.print());
        Thread t2 = new Thread(() -> obj2.print());
        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        t2.start();

        System.out.println(t1.getName() + ":" + t1.getState());
        System.out.println(t2.getName() + ":" + t2.getState());

    }
}
